A prevalent programming problem is that of advertising availability of and distributing information to interested parties without a priori knowledge of their identity. In these situations, there are two types of programs involved: a program that produces information and one or more programs that consume the information. For efficiency, it is preferable that the information producer proactively notify the interested information consumers when information becomes available, rather than force the consumer to repeatedly call or poll for available information. Yet, the identity of the consumers is not known a priori by the producer (e.g., at development or compile time of the producer program). This programming problem is encountered in a variety of situations from delivering information between very granular objects running in a single process to delivering information across an enterprise between very large applications. For example, it may be desirable to inform several thousand employees of an enterprise when its stock price reaches a given amount, or inform managers when certain tasks in a project are complete, among many other information distribution scenarios. Also, a number of network or enterprise events are of interest to many applications, such as changes in network security policy, replication failures, network printer failure, or moving server software to a new machine. There also are a large number of events that occur within a single machine and have great utility, including login/logout, network connect/disconnect, startup/shutdown, disk full, CPU idle, etc.
Traditional object-oriented programming models and systems use method calls according to a request/reply model to communicate data between objects. Object-oriented programming models, such as the Microsoft Component Object Model (“COM”), define a standard structure of software objects that can be interconnected and collectively assembled into an application (which, being assembled from component objects, is herein referred to as a “component application”). The objects are hosted in an execution environment created by system services, such as the object execution environments provided by COM. This system exposes services for use by component application objects in the form of application programming interfaces (“APIs”), system-provided objects and system-defined object interfaces.
In accordance with object-oriented programming principles, the component application is a collection of object classes which each model real world or abstract items by combining data to represent the item's properties with functions to represent the item's functionality. More specifically, an object is an instance of a programmer-defined type referred to as a class, which exhibits the characteristics of data encapsulation, polymorphism and inheritance. Data encapsulation refers to the combining of data (also referred to as properties of an object) with methods that operate on the data (also referred to as member functions of an object) into a unitary software component (i.e., the object), such that the object hides its internal composition, structure and operation and exposes its functionality to client programs that utilize the object only through one or more interfaces. An interface of the object is a group of semantically related member functions of the object. In other words, the client programs do not access the object's data directly, but must instead call functions on the object's interfaces to operate on the data.
Polymorphism refers to the ability to view (i.e., interact with) two similar objects through a common interface, thereby eliminating the need to differentiate between two objects. Inheritance refers to the derivation of different classes of objects from a base class, where the derived classes inherit the properties and characteristics of the base class.
These object-oriented programming models traditionally use a request/reply model to communicate data between objects. In the request/reply model, one object (typically the object that consumes or acts on the information) implements an incoming interface. FIG. 1, for example, shows a program listing 10 according to COM of an “IStockEvents” interface exposed by an object to receive communications of stock information, such as stock symbols and price updates. A client of the object (such as one that produces information acted on by the object) communicates with the object through the incoming interface, by first creating the object and calling a method in the interface of the object. FIG. 2, for example, shows a program listing 12 of client code to communicate stock information to the object by calling a method in the IStockEvents interface. In such request/reply models, however, there is a one-to-one relationship between the client and object. Also, the identity of the object must be “known” a priori to the client. Specifically, the relationship of client to object is “early-bound,” i.e., established at development by compiling in a class identifier (e.g., shown in FIG. 2 by the “CLSID_StockSubscriber” parameter of the “CoCreateInstance( )” call).
A more effective approach to this distribution problem (i.e., of many unknown consumers) is to use a publish and subscribe event communications model, which generally provides an information distribution mechanism that in many ways resembles the magazine publishing industry. In magazine publishing, a publisher (e.g., McGraw-Hill) advertises magazines (e.g., Byte magazine) to potential subscribers. Subscriptions to a given magazine are placed by the subscriber or possibly also by another person on behalf of the subscriber. The subscription is an agreement by the publisher to distribute an event (e.g., issues of the magazine) to the subscriber when the event is produced by the publisher. Publish and subscribe event communications models use a similar approach for distributing information from a producing program (the “publisher”) to a consuming program (the “subscriber”).
Two representative publish and subscribe event communications models are Sun Microsystems' JavaBeans™ Events architecture and the Connection Points model of Microsoft Corporation's Component Object Model (hereafter, COM Connection Points). Both of these feature an internal events model in which the eventing semantic is completely contained within the scope of the publisher and subscriber. In other words, the subscriber contains the program code that implements the subscription logic.
More specifically, JavaBeans™ Events defines structures of a set of interfaces to be implemented by the publisher and subscriber, as well as an event object with standard interfaces. The publisher (called an “event source”) identifies itself as sourcing particular events by implementing registration methods that conform to a specific design pattern (e.g., declared as “FooListener AddFooListener(FooListener fel);” in the Java programming language) and accept references to instances of particular subscriber's (called an “event listener”) interfaces. The information produced by the event source is encapsulated in an “event state” object (which is derived as a subclass from “java.util.EventObject”). The subscriber identifies itself as interested in particular events by implementing some set of “event listener” interfaces with methods that receive the event state object. The subscriber must include code to call the registration method of the publisher and pass a reference to its event listener interface. The publisher, on the other hand, contains code that, upon producing information which is to be published, creates an event state object to contain the information and calls an appropriate method on the registered event listener interfaces of the event listener to which the event state object is passed.
In COM Connection Points, a publisher (called a “connectable object”) defines outgoing interfaces and provides an associated “connection point” sub-object for distributing information to interested “client objects” or subscribers. The connectable object implements an interface, “IConnectionPointContainer,” which indicates the existence of outgoing interfaces. The client object uses the “IConnectionPointContainer” interface to obtain a reference to an interface, “IConnectionPoint,” of the connection point sub-object for a desired outgoing interface (specified by an interface identifier or “IID”) defined by the connectable object. Using the “IConnectionPoint” interface, the client object establishes an advisory connection with the connectable object by passing in a reference to a sink object that implements the desired outgoing interface. The connectable object then publishes an event via calls to the connected outgoing interfaces.
In both the JavaBeans™ Events and COM Connection Points models, the subscriber is required to contain “subscription logic” (i.e., program code) that establishes the subscription or connection from the publisher to the subscriber. In JavaBeans™ Events, the event listener contains logic to locate the appropriate event source that produces event state objects with desired information, and to register its event listener interface with that event source so as to receive the event state object. In COM Connection Points, the client object contains logic to locate the appropriate connectable object that produces desired information, and establish an advisory connection with the connectable object to the client object's sink interface. This need in such internal event models to incorporate subscription logic built into the publisher and/or subscriber complicates the programming model, making it more difficult for developers to write application objects that make use of the event model for information distribution.
A further disadvantage of these event communications models is that the publisher and subscriber are required to have concurrent lifetimes. In JavaBeans™ Events, the event listener registers an “event listener” interface reference with the event source. In COM Connection Points, the client object passes a sink object interface reference to the connection point object to establish the advisory connection from the connectable object. In either case, the interface reference is only valid during the lifetime of the event listener or client sink object, requiring that the subscriber and publisher be in existence (i.e., instantiated) when the subscription is made and when the event is published.